from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.query import QuerySet

__all__ = ['JSONEncoder', 'JsonResponse', 'json_view']

def maybe_call(x):
    if callable(x): return x()
    return x


class JSONEncoder(DjangoJSONEncoder):
    '''An extended JSON encoder to handle some additional cases.

    The Django encoder already deals with date/datetime objects.
    Additionally, this encoder uses an 'as_dict' or 'as_list' attribute or
    method of an object, if provided. It also makes lists from QuerySets.
    '''
    def default(self, obj):
        if hasattr(obj, 'as_dict'):
            return maybe_call(obj.as_dict)
        elif hasattr(obj, 'as_list'):
            return maybe_call(obj.as_list)
        elif isinstance(obj, QuerySet):
            return list(obj)
        return super(JSONEncoder, self).default(obj)
json_encode = JSONEncoder().encode


from django.http import HttpResponse
class JsonResponse(HttpResponse):
    '''Like an HttpResponse, but encodes the data as JSON.
    By default, the encoded data is surrounded in HTML comments
    to avoid CSRF issues.
    
    The file-like operations probably won't do what you want.'''
    def __init__(self, obj, format_string='/* %s */', **kw):
        json = json_encode(obj)
        if format_string is not None:
            json = format_string % (json,)
        super(JsonResponse, self).__init__(json, mimetype='application/json', **kw)


from django.utils.decorators import wraps
def json_view(func):
    '''A view decorator that returns a dict result as JSON.'''
    @wraps(func)
    def wrap(request, *a, **kw):
        response = func(request, *a, **kw)
        if isinstance(response, dict):
            return JsonResponse(response)
        else:
            return response

    return wrap
